using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

namespace FShaderInspector
{
    public class MaterialCleanupMenu
    {
        [MenuItem("Window/FTools/Shader Inspector/材质球清理", true, 2001)]
        static bool MenuCheckbox()
        {
            // 检查选中的是否全部为 material
            return Selection.objects.Length > 0 && Selection.objects.All(obj => obj is Material);
        }

        [MenuItem("Window/FTools/Shader Inspector/材质球清理", false, 2002)]
        public static void Cleanup()
        {
            AssetDatabase.SaveAssets();
            var materials = Selection.objects.Select(mat => mat as Material).ToArray();
            ClearMaterial(materials);
        }

        /// <summary> 清理材质球 </summary>
        /// <param name="materials">清理材质球数组</param>
        public static void ClearMaterial(Material[] materials)
        {
            // 父节点层级
            var parentLevel = 3;
            // 父层级启用状态
            var parentEnabled = true;

            Undo.undoRedoPerformed += OnClearUndoCallback;

            foreach (var mat in materials)
            {
                // 撤销记录
                Undo.RecordObject(mat,"CLEANUP_MATERIAL_REDUNDANCY");

                var propertyCount = mat.shader.GetPropertyCount();
                for (int i = 0; i < propertyCount; i++)
                {
                    // 获取属性名称、类型、标签数组
                    var propName = mat.shader.GetPropertyName(i);
                    var propType = mat.shader.GetPropertyType(i);
                    var attributes = mat.shader.GetPropertyAttributes(i);
                    var flags = mat.shader.GetPropertyFlags(i);

                    // 隐藏纹理 则直接设为 null
                    if (flags == ShaderPropertyFlags.HideInInspector && propType == ShaderPropertyType.Texture)
                        mat.SetTexture(propName, null);

                    foreach (var attribute in attributes)
                    {
                        var attributeSp = attribute.Split('(');
                        var labelName = attributeSp[0];
                        var labelProp = StringUtils.GetLittleBracketsValue(attribute).Split(',');

                        if (labelName == FShaderGUI.FoldoutDrawer)
                        {
                            parentLevel = 1;
                            parentEnabled = true;
                        }

                        // 选择标签层级默认为最高level 1
                        if (labelName == FShaderGUI.SelectDrawer)
                        {
                            parentLevel = 1;
                            parentEnabled = (int)mat.GetFloat(propName) == 1;
                        }

                        if (labelName == FShaderGUI.EnableDrawer)
                        {
                            // 默认标签不填入值 level = 1
                            // 填入值则正常转换 level
                            var currentLevel = labelProp.Length == 1 ? 1 : int.Parse(labelProp[1]);

                            // 2 > 1 为子层级
                            if (currentLevel > parentLevel)
                            {
                                // 父层级关闭 子层级也跟随关闭
                                if (!parentEnabled)
                                {
                                    FShaderGUI.TryDisableKeyword(mat, labelProp[0]);
                                    mat.SetFloat(propName, 0);
                                }
                            }
                            // 相等或大于的情况下设置新的层级等级和状态
                            else
                            {
                                parentLevel = currentLevel;
                                parentEnabled = (int)mat.GetFloat(propName) == 1;
                            }
                        }

                        if (labelName == FShaderGUI.BreakDrawer)
                        {
                            // 只有返回最大层级时，设置为父层级开启状态
                            var level = int.Parse(StringUtils.GetLittleBracketsValue(attribute));
                            if (level == 1)
                            {
                                parentLevel = level;
                                parentEnabled = true;
                            }
                        }

                        if (labelName == FShaderGUI.NonzeroDrawer)
                        {
                            // 无标签等级
                            if (!parentEnabled)
                            {
                                FShaderGUI.TryDisableKeyword(mat, labelProp[0]);
                                if (propType == ShaderPropertyType.Int || propType == ShaderPropertyType.Float || propType == ShaderPropertyType.Range)
                                    mat.SetFloat(propName, 0);
                                else if (propType == ShaderPropertyType.Vector)
                                    mat.SetVector(propName, Vector4.zero);
                            }
                        }

                        if (labelName == FShaderGUI.ShowIfDrawer)
                        {
                            // 设置父节点未开启
                            if (!mat.shaderKeywords.Contains(labelProp[0]))
                                ResetProperty(propType, mat, i, propName);
                        }

                        if (labelName == FShaderGUI.HideIfDrawer)
                        {
                            // 设置父节点未开启
                            if (mat.shaderKeywords.Contains(labelProp[0]))
                                ResetProperty(propType, mat, i, propName);
                        }
                    }

                    if (!parentEnabled)
                        ResetProperty(propType, mat, i, propName);
                }

                CleanupRedundancy(mat);
            }

            // 重置属性
            void ResetProperty(ShaderPropertyType propType, Material mat, int i, string propName)
            {
                switch (propType)
                {
                    case ShaderPropertyType.Color:
                        var defaultColor = mat.shader.GetPropertyDefaultVectorValue(i);
                        mat.SetColor(propName, defaultColor);
                        break;
                    case ShaderPropertyType.Vector:
                        var defaultVector = mat.shader.GetPropertyDefaultVectorValue(i);
                        mat.SetVector(propName, defaultVector);
                        break;
                    case ShaderPropertyType.Float:
                        var defaultFloat = mat.shader.GetPropertyDefaultFloatValue(i);
                        mat.SetFloat(propName, defaultFloat);
                        break;
                    case ShaderPropertyType.Range:
                        var defaultRange = mat.shader.GetPropertyDefaultFloatValue(i);
                        mat.SetFloat(propName, defaultRange);
                        break;
                    case ShaderPropertyType.Texture:
                        mat.SetTexture(propName, null);
                        mat.SetTextureOffset(propName, Vector2.zero);
                        mat.SetTextureScale(propName, Vector2.one);
                        break;
                    case ShaderPropertyType.Int:
                        var defaultInt = mat.shader.GetPropertyDefaultIntValue(i);
                        mat.SetInt(propName, defaultInt);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            AssetDatabase.SaveAssets();
            FShaderGUI.LogMessage("Material 清理！");
        }

        /// <summary> 清理撤销回调 </summary>
        static void OnClearUndoCallback()
        {
            // 撤销材质清理后，下次执行撤销时保存一次资产
            AssetDatabase.SaveAssets();
            Undo.undoRedoPerformed -= OnClearUndoCallback;
        }

        /// <summary> 清理材质球冗余 </summary>
        /// <param name="mat">目标材质球</param>
        static void CleanupRedundancy(Material mat)
        {
            if (FShaderGUI.GetShaderKeywords(mat.shader, out var global, out var local))
            {
                // 获取着色器关键字
                var keywords = new HashSet<string>();
                foreach (var g in global)
                    keywords.Add(g);

                foreach (var l in local)
                    keywords.Add(l);

                // 获取开启的宏
                var enableKeywords = new List<string>(mat.shaderKeywords);

                // 清理不存在的宏
                foreach (var item in mat.shaderKeywords)
                {
                    if (!keywords.Contains(item))
                        enableKeywords.Remove(item);
                }
                mat.shaderKeywords = enableKeywords.ToArray();
            }

            // 获取属性
            var property = new HashSet<string>();
            var propertyCount = mat.shader.GetPropertyCount();

            for (int i = 0; i < propertyCount; i++)
                property.Add(mat.shader.GetPropertyName(i));

            var oMat = new SerializedObject(mat);
            var disabledShaderPasses = oMat.FindProperty("disabledShaderPasses");
            var savedProperties = oMat.FindProperty("m_SavedProperties");
            var texEnvs = savedProperties.FindPropertyRelative("m_TexEnvs");
            var floats = savedProperties.FindPropertyRelative("m_Floats");
            var colors = savedProperties.FindPropertyRelative("m_Colors");

            // 对比属性删除残留的属性
            for (int i = disabledShaderPasses.arraySize - 1; i >= 0; i--)
            {
                // Pass 通道属性
                if (!property.Contains(disabledShaderPasses.GetArrayElementAtIndex(i).displayName))
                    disabledShaderPasses.DeleteArrayElementAtIndex(i);
            }

            for (int i = texEnvs.arraySize - 1; i >= 0; i--)
            {
                if (!property.Contains(texEnvs.GetArrayElementAtIndex(i).displayName))
                    texEnvs.DeleteArrayElementAtIndex(i);
            }

            for (int i = floats.arraySize - 1; i >= 0; i--)
            {
                if (!property.Contains(floats.GetArrayElementAtIndex(i).displayName))
                    floats.DeleteArrayElementAtIndex(i);
            }

            for (int i = colors.arraySize - 1; i >= 0; i--)
            {
                if (!property.Contains(colors.GetArrayElementAtIndex(i).displayName))
                    colors.DeleteArrayElementAtIndex(i);
            }

            oMat.ApplyModifiedProperties();
        }
    }
}